package darklaunch

import (
	"context"
	"fmt"
	"sync"
	"time"
)

const defaultRefreshInterval = time.Minute

type DarkLaunch struct {
	mutex        sync.RWMutex
	darkrules    *DarkConfig
	customerDark map[string]DarkFeature
	loader       Loader
	parser       Parser
	ctx          context.Context
	cancel       context.CancelFunc
}

func New(loader Loader, parser Parser, hotLoad bool) *DarkLaunch {

	ctx, cancel := context.WithCancel(context.Background())
	d := &DarkLaunch{
		darkrules: nil,
		loader:    loader,
		parser:    parser,
		cancel:    cancel,
		ctx:       ctx,
	}
	d.setDarkRules()
	if hotLoad {
		go d.HotLoadDarkRules()
	}
	return d
}

func (d *DarkLaunch) HotLoadDarkRules() {
	ticker := time.NewTicker(defaultRefreshInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			d.setDarkRules()
		case <-d.ctx.Done():
			return
		}
	}
}

func (d *DarkLaunch) setDarkRules() {
	content, err := d.loader.Load()
	if err != nil {
		panic(nil)
	}

	config, err := d.parser.Parse(content)
	if err != nil {
		panic(nil)
	}

	d.mutex.Lock()
	defer d.mutex.Unlock()
	d.darkrules = config
}

func (d *DarkLaunch) AddDarkFeature(key string, feature DarkFeature) error {
	d.mutex.Lock()
	defer d.mutex.Unlock()
	// Check if key already exists in customerDark or darkrules.
	_, existsInCustomer := d.customerDark[key]
	_, existsInDarkrules := d.darkrules.Configs[key]
	if existsInCustomer || existsInDarkrules {
		return fmt.Errorf("key %s already exists", key)
	}

	d.customerDark[key] = feature
	return nil
}

func (d *DarkLaunch) GetDarkRule(key string) DarkFeature {
	d.mutex.RLock()
	defer d.mutex.RUnlock()

	ret := d.darkrules.Configs[key]
	if ret != nil {
		return ret
	}
	return d.customerDark[key]
}

func (d *DarkLaunch) StopHotLoad() {
	d.cancel() // call the cancellation function to stop HotLoadDarkRules goroutine.
}
